//
//  SK_MASConstraintMaker.m
//  SK_Masonry
//
//  Created by Jonas Budelmann on 20/07/13.
//  Copyright (c) 2013 cloudling. All rights reserved.
//

#import "SK_MASConstraintMaker.h"
#import "SK_MASViewConstraint.h"
#import "SK_MASCompositeConstraint.h"
#import "SK_MASConstraint+Private.h"
#import "SK_MASViewAttribute.h"
#import "View+SK_MASAdditions.h"

@interface SK_MASConstraintMaker () <SK_MASConstraintDelegate>

@property (nonatomic, weak) SK_MAS_VIEW *view;
@property (nonatomic, strong) NSMutableArray *constraints;

@end

@implementation SK_MASConstraintMaker

- (id)initWithView:(SK_MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    self.view = view;
    self.constraints = NSMutableArray.new;
    
    return self;
}

- (NSArray *)install {
    if (self.removeExisting) {
        NSArray *installedConstraints = [SK_MASViewConstraint installedConstraintsForView:self.view];
        for (SK_MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }
    NSArray *constraints = self.constraints.copy;
    for (SK_MASConstraint *constraint in constraints) {
        constraint.updateExisting = self.updateExisting;
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}

#pragma mark - SK_MASConstraintDelegate

- (void)constraint:(SK_MASConstraint *)constraint shouldBeReplacedWithConstraint:(SK_MASConstraint *)replacementConstraint {
    NSUInteger index = [self.constraints indexOfObject:constraint];
    NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
    [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
}

- (SK_MASConstraint *)constraint:(SK_MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    SK_MASViewAttribute *viewAttribute = [[SK_MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    SK_MASViewConstraint *newConstraint = [[SK_MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    if ([constraint isKindOfClass:SK_MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        SK_MASCompositeConstraint *compositeConstraint = [[SK_MASCompositeConstraint alloc] initWithChildren:children];
        compositeConstraint.delegate = self;
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    if (!constraint) {
        newConstraint.delegate = self;
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

- (SK_MASConstraint *)addConstraintWithAttributes:(SK_MASAttribute)attrs {
    __unused SK_MASAttribute anyAttribute = (SK_MASAttributeLeft | SK_MASAttributeRight | SK_MASAttributeTop | SK_MASAttributeBottom | SK_MASAttributeLeading
                                          | SK_MASAttributeTrailing | SK_MASAttributeWidth | SK_MASAttributeHeight | SK_MASAttributeCenterX
                                          | SK_MASAttributeCenterY | SK_MASAttributeBaseline
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
                                          | SK_MASAttributeFirstBaseline | SK_MASAttributeLastBaseline
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
                                          | SK_MASAttributeLeftMargin | SK_MASAttributeRightMargin | SK_MASAttributeTopMargin | SK_MASAttributeBottomMargin
                                          | SK_MASAttributeLeadingMargin | SK_MASAttributeTrailingMargin | SK_MASAttributeCenterXWithinMargins
                                          | SK_MASAttributeCenterYWithinMargins
#endif
                                          );
    
    NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)");
    
    NSMutableArray *attributes = [NSMutableArray array];
    
    if (attrs & SK_MASAttributeLeft) [attributes addObject:self.view.mas_left];
    if (attrs & SK_MASAttributeRight) [attributes addObject:self.view.mas_right];
    if (attrs & SK_MASAttributeTop) [attributes addObject:self.view.mas_top];
    if (attrs & SK_MASAttributeBottom) [attributes addObject:self.view.mas_bottom];
    if (attrs & SK_MASAttributeLeading) [attributes addObject:self.view.mas_leading];
    if (attrs & SK_MASAttributeTrailing) [attributes addObject:self.view.mas_trailing];
    if (attrs & SK_MASAttributeWidth) [attributes addObject:self.view.mas_width];
    if (attrs & SK_MASAttributeHeight) [attributes addObject:self.view.mas_height];
    if (attrs & SK_MASAttributeCenterX) [attributes addObject:self.view.mas_centerX];
    if (attrs & SK_MASAttributeCenterY) [attributes addObject:self.view.mas_centerY];
    if (attrs & SK_MASAttributeBaseline) [attributes addObject:self.view.mas_baseline];
    
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    
    if (attrs & SK_MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
    if (attrs & SK_MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
    
#endif
    
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
    
    if (attrs & SK_MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin];
    if (attrs & SK_MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin];
    if (attrs & SK_MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin];
    if (attrs & SK_MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin];
    if (attrs & SK_MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin];
    if (attrs & SK_MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin];
    if (attrs & SK_MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins];
    if (attrs & SK_MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins];
    
#endif
    
    NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count];
    
    for (SK_MASViewAttribute *a in attributes) {
        [children addObject:[[SK_MASViewConstraint alloc] initWithFirstViewAttribute:a]];
    }
    
    SK_MASCompositeConstraint *constraint = [[SK_MASCompositeConstraint alloc] initWithChildren:children];
    constraint.delegate = self;
    [self.constraints addObject:constraint];
    return constraint;
}

#pragma mark - standard Attributes

- (SK_MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

- (SK_MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (SK_MASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

- (SK_MASConstraint *)right {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}

- (SK_MASConstraint *)bottom {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom];
}

- (SK_MASConstraint *)leading {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading];
}

- (SK_MASConstraint *)trailing {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing];
}

- (SK_MASConstraint *)width {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];
}

- (SK_MASConstraint *)height {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];
}

- (SK_MASConstraint *)centerX {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX];
}

- (SK_MASConstraint *)centerY {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY];
}

- (SK_MASConstraint *)baseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline];
}

- (SK_MASConstraint *(^)(SK_MASAttribute))attributes {
    return ^(SK_MASAttribute attrs){
        return [self addConstraintWithAttributes:attrs];
    };
}

#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)

- (SK_MASConstraint *)firstBaseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline];
}

- (SK_MASConstraint *)lastBaseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline];
}

#endif


#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)

- (SK_MASConstraint *)leftMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin];
}

- (SK_MASConstraint *)rightMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin];
}

- (SK_MASConstraint *)topMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin];
}

- (SK_MASConstraint *)bottomMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin];
}

- (SK_MASConstraint *)leadingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin];
}

- (SK_MASConstraint *)trailingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin];
}

- (SK_MASConstraint *)centerXWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins];
}

- (SK_MASConstraint *)centerYWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins];
}

#endif


#pragma mark - composite Attributes

- (SK_MASConstraint *)edges {
    return [self addConstraintWithAttributes:SK_MASAttributeTop | SK_MASAttributeLeft | SK_MASAttributeRight | SK_MASAttributeBottom];
}

- (SK_MASConstraint *)size {
    return [self addConstraintWithAttributes:SK_MASAttributeWidth | SK_MASAttributeHeight];
}

- (SK_MASConstraint *)center {
    return [self addConstraintWithAttributes:SK_MASAttributeCenterX | SK_MASAttributeCenterY];
}

#pragma mark - grouping

- (SK_MASConstraint *(^)(dispatch_block_t group))group {
    return ^id(dispatch_block_t group) {
        NSInteger previousCount = self.constraints.count;
        group();

        NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)];
        SK_MASCompositeConstraint *constraint = [[SK_MASCompositeConstraint alloc] initWithChildren:children];
        constraint.delegate = self;
        return constraint;
    };
}

@end
